// { dg-do run }
// { dg-options "-Wno-attribute-alias -fno-strict-aliasing" }
// Origin: Mark Mitchell <mark@codesourcery.com>

#if defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100

#include <stddef.h>

struct S0
{
  virtual void s0 ();
};

struct S1 : virtual public S0
{
  virtual void s1 ();
};

struct S2 : virtual public S1
{
  virtual void s1 ();
  virtual void s0 ();
};

struct S3
{
  virtual void s3 ();
};

struct S4 : public S3, virtual public S2
{
  virtual void s1 ();
};

void S0::s0 ()
{
}

void S1::s1 ()
{
}

void S2::s1 ()
{
}

void S2::s0 ()
{
}

void S3::s3 ()
{
}

void S4::s1 ()
{
}

/* The vtables should look like:

   S0 primary vtable
   
     S0 offset to top
     S0 RTTI
     S0::s0

   =================

   S1 primary vtable

     S0::s0 vcall offset
     S0 vbase offset
     S1 offset to top
     S1 RTTI
     S0::s0
     S1::s1

   =================

   S2 primary vtable
   
     S2::s1 vcall offset
     S1 vbase offset
     S2::s0 vcall offset
     S0 vbase offset
     S2 offset to top
     S2 RTTI
     S2::s0
     S2::s1

   =================

   S3 primary vtable

     S3 offset to top
     S3 RTTI
     S3::s3

   =================

   S4 primary vtable

     vbase offset for S0
     vbase offset for S1
     vbase offset for S2
     S4 offset to top
     S4 RTTI
     S3::s3
     S4::s1

   S2-in-S4 secondary vtable

     S1 vbase offset
     S4::s1 vcall offset
     S0 vbase offset
     S2:s0 vcall offset
     S2 offset to top
     S4 RTTI
     S2::s0
     S4::s1

*/

// These are tricks to allow us to get raw function pointers for
// member functions.
extern "C" {
  /* We can use weakref here without dg-require-weak, because we know
     the symbols are defined, so we don't actually issue the .weak
     directives.  The references to the incompatible virtual S3::s3()
     and S4::s1() trigger -Wattributes.  */
  static void S3_s3 () __attribute__((__weakref__ ("_ZN2S32s3Ev")));
  static void S4_s1 () __attribute__((__weakref__ ("_ZN2S42s1Ev")));
}

// IA-64 uses function descriptors not function pointers in its vtables.
#if defined __ia64__
#define CMP_VPTR(A, B)	(*(void **)(A) == *(void **)(B))
#ifdef _LP64
#define INC_VPTR(A)	((A) += 2)
#define INC_VDATA(A,N)	((A) += (N))
#else
#define INC_VPTR(A)	((A) += 4)
#define INC_VDATA(A,N)	((A) += 2*(N))
#endif
#else
// HPPA uses function pointers but they point to function descriptors.
#if defined __hppa__
#ifdef __hpux__
#ifdef _LP64
#define CMP_VPTR(A, B)	(*(unsigned long *)(*(A)+16) == *(unsigned long *)((unsigned long)(B)+16))
#else
#define CMP_VPTR(A, B)	(*(A) == (ptrdiff_t)(B))
#endif /* _LP64 */
#else
extern "C" { unsigned int __canonicalize_funcptr_for_compare (void*); }
#define CMP_VPTR(A, B) (__canonicalize_funcptr_for_compare(*(void **)A) == __canonicalize_funcptr_for_compare((void *)B))
#endif /* __hpux__ */
#else
#define CMP_VPTR(A, B)	(*(A) == (ptrdiff_t)(B))
#endif /* __hppa__ */
#define INC_VPTR(A)	((A) += 1)
#define INC_VDATA(A,N)	((A) += (N))
#endif /* __ia64__ */

int main ()
{
  S4 s4;
  ptrdiff_t **vptr;
  ptrdiff_t *vtbl;

  // Set vtbl to point at the beginning of S4's primary vtable.
  vptr = (ptrdiff_t **) &s4;
  vtbl = *vptr;
  INC_VDATA (vtbl, -5);

  if (*vtbl != ((char*) (S0*) &s4) - (char*) &s4)
    return 1;
  INC_VDATA (vtbl, 1);
  if (*vtbl != ((char*) (S1*) &s4) - (char*) &s4)
    return 2;
  INC_VDATA (vtbl, 1);
  if (*vtbl != ((char*) (S2*) &s4) - (char*) &s4)
    return 3;
  INC_VDATA (vtbl, 1);
  if (*vtbl != 0)
    return 4;
  INC_VDATA (vtbl, 1);
  // Skip the RTTI entry.
  INC_VDATA (vtbl, 1);
  if (! CMP_VPTR (vtbl, &S3_s3))
    return 5;
  INC_VPTR (vtbl);
  if (! CMP_VPTR (vtbl, &S4_s1))
    return 6;
  INC_VPTR (vtbl);
  // The S1 vbase offset.
  if (*vtbl != 0)
    return 7;
  INC_VDATA (vtbl, 1);
  // The S4::s1 vcall offset is negative; once you convert to S2, you
  // have to convert to S4 to find the final overrider.
  if (*vtbl != ((char*) &s4 - (char*) (S2*) &s4))
    return 8;
  INC_VDATA (vtbl, 1);
  if (*vtbl != 0)
    return 9;
  INC_VDATA (vtbl, 1);
  if (*vtbl != 0)
    return 10;
  INC_VDATA (vtbl, 1);
  // Now we're at the S2 offset to top entry.
  if (*vtbl != ((char*) &s4 - (char*) (S2*) &s4))
    return 11;
  INC_VDATA (vtbl, 1);
  // Skip the RTTI entry.
  INC_VDATA (vtbl, 1);
  // Skip the remaining virtual functions -- they are thunks.
  INC_VPTR (vtbl);
  INC_VPTR (vtbl);
}

#else /* !(defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100) */

int main ()
{
}

#endif /* !(defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100) */
